package com.mysimpatico.sqlwrapper;

import com.mysimpatico.sqlwrapper.SqlWrapper.Type;


/**
 * 
 * @author Gabriele Kahlout
 * 
 *	Class for immutable object Column.
 *
 *  Type lengths is not supported in Java DB excepts for VARCHAR.
 */

//TODO: consider inheriting from a parent class with common constructor factored out.

public class Column {

	private final String name;
	private final Type type;
	protected final int typeLength;
	private final Object def;
	private final Table refTable;
	final Column refField;
	private final boolean unique;
	private final boolean notNull;
	private final String condition;
	

	public String getName(){
		return name;
	}

	public String getName(Table table){
		return (table == null)? getName() : table.getName() + "." +name;
	}

	public SqlWrapper.Type getType(){
		return type;
	}

	Object getDef(){
		return def;
	}

	protected Table getRefTable(){
		return refTable;
	}

	protected Column getRefField(){
		return refField;
	}

	public Column(final boolean unique, final String name, final SqlWrapper.Type type){
		this.name = name;
		this.type = type;
		def = null;
		refTable = null;
		refField = null;
		this.unique = unique;
		typeLength = 0;
		notNull = false;
		condition = null;
	}
	
	public Column(final String name){
		this.name = name;
		type = SqlWrapper.Type.INT;
		def = null;
		refTable = null;
		refField = null;
		unique = false;
		typeLength = 0;
		notNull = false;
		condition = null;
	}

	public Column(final String name, final SqlWrapper.Type type, final boolean def){
		this.name = name;
		this.type = type;
		
		if (type.equals(SqlWrapper.Type.SMALLINT)) this.def = (def)? 1:0;
		else this.def = def;
		
		refTable = null;
		refField = null;
		unique = false;
		typeLength = 0;
		notNull = false;
		condition = null;
	}

	public Column(final String name, final SqlWrapper.Type type, final boolean def, final boolean notNull){
		this.name = name;
		this.type = type;
		
		if (type.equals(SqlWrapper.Type.SMALLINT)) this.def = (def)? 1:0;
		else this.def = def;
		
		refTable = null;
		refField = null;
		unique = false;
		typeLength = 0;
		this.notNull = notNull;
		condition = null;
	}

	public Column(final String name, final int length, final SqlWrapper.Type type, final Object def){
		this.name = name;
		this.type = type;
		this.def = def;
		refTable = null;
		refField = null;
		unique = false;
		typeLength = length;
		notNull = false;
		condition = null;
	}

	public Column(final String name, final int length, final SqlWrapper.Type type, final Object def, final boolean notNull){
		this.name = name;
		this.type = type;
		this.def = def;
		refTable = null;
		refField = null;
		unique = false;
		typeLength = length;
		this.notNull = notNull;
		condition = null;
	}
	
	public Column(final String name, final Table refTable, final Column refField,final boolean notNull, final Column checkVal){
		this.name = name;
		type = refField.getType();
		this.refTable = refTable;
		this.refField = refField;
		def = refField.getDef(); /* pointer bound to problems with primitives */
		unique = false;
		typeLength = 0;
		this.notNull = notNull;
		condition = name + '=' + checkVal ;
	}
	
	public Column(final String name, final Table refTable, final Column refField,final boolean notNull, final Column checkVal, final char operator, final int val){
		this.name = name;
		type = refField.getType();
		this.refTable = refTable;
		this.refField = refField;
		def = refField.getDef(); /* pointer bound to problems with primitives */
		unique = false;
		typeLength = 0;
		this.notNull = notNull;
		condition = name + '=' + checkVal + operator + val;
	}

        public Column(final String name, final SqlWrapper.Type type, final boolean notNull, final String operator, final String val){
		this.name = name;
		this.type = type;
		refTable = null;
		refField = null;
		def = null; /* pointer bound to problems with primitives */
		unique = false;
		typeLength = 0;
		this.notNull = notNull;
                condition = name +  operator + "'" + val + "'";
	}

	public Column(final String name, final int length, final SqlWrapper.Type type, final Object def, final boolean notNull,
			final String[] colNames, final SqlWrapper.Type[] colTypes, final String[] values, final char[] operators, final SqlWrapper.Condition[] conditions){
		this.name = name;
		this.type = type;
		this.def = def;
		refTable = null;
		refField = null;
		unique = false;
		typeLength = length;
		this.notNull = notNull;
		this.condition = SqlWrapper.joinWhereClauses(null, colNames, colTypes, values, operators, conditions);
	}

	public Column(final String name, final int length, final SqlWrapper.Type type, final Object def,
			final String[] values){
		this.name = name;
		this.type = type;
		this.def = def;
		refTable = null;
		refField = null;
		unique = false;
		typeLength = length;
		notNull = true;
		final String[] colNames = new String[values.length];
		final SqlWrapper.Type[] colTypes = new SqlWrapper.Type[values.length];
		for (int i=0; i<colNames.length; i++) colNames[i] = name;

		this.condition = SqlWrapper.joinWhereClauses(colNames, colTypes, values, false);
	}

	public Column(final String name, final int length, final SqlWrapper.Type type, final boolean notNull,
			final String[] values){
		this.name = name;
		this.type = type;
		this.def = null;
		refTable = null;
		refField = null;
		unique = false;
		typeLength = length;
		this.notNull = notNull;
		final String[] colNames = new String[values.length];
		final SqlWrapper.Type[] colTypes = new SqlWrapper.Type[values.length];
		for (int i=0; i<colNames.length; i++){
			colNames[i] = name;
			colTypes[i] = type;
		}
		this.condition = SqlWrapper.joinWhereClauses(colNames, colTypes, values, false);
	}

	public Column(final String name, final int length, final SqlWrapper.Type type, final String[] values){
		this.name = name;
		this.type = type;
		this.def = null;
		refTable = null;
		refField = null;
		unique = false;
		typeLength = length;
		this.notNull = false;
		final String[] colNames = new String[values.length];
		final SqlWrapper.Type[] colTypes = new SqlWrapper.Type[values.length];
		for (int i=0; i<colNames.length; i++){
			colNames[i] = name;
			colTypes[i] = type;
		}
		this.condition = SqlWrapper.joinWhereClauses(colNames, colTypes, values, false);
	}
	
	public Column(final String name, final int length, final SqlWrapper.Type type, final char[] values){
		this.name = name;
		this.type = type;
		this.def = null;
		refTable = null;
		refField = null;
		unique = false;
		typeLength = length;
		this.notNull = false;
		final String[] colNames = new String[values.length];
		final SqlWrapper.Type[] colTypes = new SqlWrapper.Type[values.length];
		for (int i=0; i<colNames.length; i++){
			colNames[i] = name;
			colTypes[i] = type;
		}
		this.condition = SqlWrapper.joinWhereClauses(colNames, colTypes, values, false);
	}
	
	public Column(final String name, final int length, final SqlWrapper.Type type, final int[] vals){
		this.name = name;
		this.type = type;
		this.def = null;
		refTable = null;
		refField = null;
		unique = false;
		typeLength = length;
		this.notNull = false;
		final String[] colNames = new String[vals.length];
		final SqlWrapper.Type[] colTypes = new SqlWrapper.Type[vals.length];
		for (int i=0; i<colNames.length; i++){
			colNames[i] = name;
			colTypes[i] = type;
		}
		this.condition = SqlWrapper.joinWhereClauses(colNames, colTypes, vals, false);
	}

	public Column(final String name, final SqlWrapper.Type type, final Object def, final boolean notNull, final char operator, final int val){
		this.name = name;
		this.type = type;
		this.def = def;
		refTable = null;
		refField = null;
		unique = false;
		typeLength = 0;
		this.notNull = notNull;
		this.condition = name + operator + val;
	}
	
	
	
	public Column(final String name, final SqlWrapper.Type type,final boolean notNull, final char operator, final int val){
		this.name = name;
		this.type = type;
		def = null;
		refTable = null;
		refField = null;
		unique = false;
		typeLength = 0;
		this.notNull = notNull;
		this.condition = name + operator + val;		
	}

	public Column(final String name, final SqlWrapper.Type type, final Object def){
		this.name = name;
		this.type = type;
		this.def = def;
		refTable = null;
		refField = null;
		unique = false;
		typeLength = 0;
		notNull = false;
		condition = null;
	}

	public Column(final String name, final Table refTable, final Column refField){
		this.name = name;
		type = refField.getType();
		this.refTable = refTable;
		this.refField = refField;
		def = refField.getDef(); /* pointer bound to problems with primitives */
		unique = false;
		typeLength = refField.typeLength;
		notNull = false;
		condition = null;
	}
	
	public Column(final String name, final Table refTable){
		this(name, refTable, refTable.getIdColumn());
	}
	
	public Column(final String name, final Table refTable, final boolean notNull){
		this.name = name;
		type = refTable.getIdColumn().getType();
		this.refTable = refTable;
		this.refField = refTable.getIdColumn();
		def = refField.getDef(); /* pointer bound to problems with primitives */
		unique = false;
		typeLength = 0;
		this.notNull = notNull;
		condition = null;
	}

	public Column(final String name, final Table refTable, final Column refField,final boolean notNull){
		this.name = name;
		type = refField.getType();
		this.refTable = refTable;
		this.refField = refField;
		def = refField.getDef(); /* pointer bound to problems with primitives */
		unique = false;
		typeLength = 0;
		this.notNull = notNull;
		condition = null;
	}

	public Column(final String name, final boolean unique, final Table refTable, final Column refField,final boolean notNull){
		this.name = name;
		type = refField.getType();
		this.refTable = refTable;
		this.refField = refField;
		def = refField.getDef(); /* pointer bound to problems with primitives */
		this.unique = unique;
		typeLength = 0;
		this.notNull = notNull;
		condition = null;
	}

	public Column(final String name, SqlWrapper.Type type, final Object def, final Table refTable, final Column refField){
		this.name = name;
		this.type = type;
		this.refTable = refTable;
		this.refField = refField;
		this.def = def;
		unique = false;
		typeLength = 0;
		notNull = false;
		condition = null;
	}

	public Column(final String name, SqlWrapper.Type type){
		this.name = name;
		this.type = type;
		def = null;
		refTable = null;
		refField = null;
		unique = false;
		typeLength = 0;
		notNull = false;
		condition = null;
	}
		
	public Column(final boolean unique, final String name, Type type, final boolean notNull){
		this.name = name;
		this.type = type;
		def = null;
		refTable = null;
		refField = null;
		this.unique = unique;
		typeLength = 0;
		this.notNull = notNull;
		condition = null;
	}
	
	public Column(final String name, final Type type, final String checkQuery, final Boolean exists){
		this.name = name;
		this.type = type;
		condition = (exists != null)?(((exists)? "NOT": "") + " EXISTS( " ): "" + checkQuery + ")";
		unique = false;
		typeLength = 0;
		notNull = false;
		refTable = null;
		refField = null;
		def = null;
	}

	public Column(final String name, SqlWrapper.Type type, final int length){
		this.name = name;
		this.type = type;
		def = null;
		refTable = null;
		refField = null;
		unique = false;
		typeLength = length;
		notNull = false;
		condition = null;
	}

	private String getDefString(){
		if (def == null) return "";
		if (type == SqlWrapper.TEXT || type == SqlWrapper.CHAR || type == SqlWrapper.VARCHAR) return " DEFAULT \""+def.toString()+"\"";
		return " DEFAULT " + def.toString();
	}

	private String getLength(){
		if (typeLength != 0) return "("+typeLength+")";
		else return "";
	}

	private String getNotNull(){
		if (notNull) return " NOT NULL";
		else return "";
	}

	private String getUnique(){
		if (unique) return " UNIQUE";
		else return "";
	}

	private String getReference(){
		if (refField != null) return " REFERENCES " + refTable.getName() + "("+refField.getName() + ") " + Table.getOnDelete();
		else return "";
	}

	private String getCondition(){
		if (condition != null) return " CHECK("+ condition + ")";
		return "";
	}

	@Override
	public String toString(){
		return name + " " + type + getLength() + getDefString() +  getUnique() + getNotNull() +  getReference() + getCondition();
	}
}
